<?php
/**
 * 物流单据相关模块
 */
class PublicApiModel extends ModelsObjectPrototype {
    public $newArr = array();
	
	//指令工单指令的分类  大于100的为手动新增的指令工单 小于100的为物流系统发起的异常指令工单
	const  WORK_ORDER_SELECT_TYPE = 100;
	
	//指令工单的异常通知来源和手动新建
	const WORK_ORDER_TYPE_ADD = 1;
	const WORK_ORDER_TYPE_FROM_ABNORMAL = 2;

	//预配货记录日志长度
	const PRE_DISTRIBUTION_LOG_LENGTH = 450;
	
	//返回信息FLAG
	const SUCCESS = 0;
	const ERROR = 1;
	
	/**
	 * 根据规则获取新增指令工单信息
	 * @param array $data
	 */
	public function getWorkOrderData($id, $data, $admin_name){
		if(!empty($id)){
			$param['s_abnormal_id'] = $id;
		}else{
			$param['s_seller_type'] = $data['seller_type'];
			$param['s_seller_id'] = $data['seller_id'];
			$param['s_seller_name'] = $data['seller_name'];
			$param['s_warehouse_id'] = $data['warehouse_id'];
		}
		$param['s_admin_name'] = $admin_name;
		$res = $this->tools->callRule('Logistics_HUV1_CreateWorkOrder', $param);
		if($res['error'] || !$res['msg']) {
			return array();
		}
		$returnRes = json_decode($res['msg'][0]['h_work_order'], true);
		$returnRes['note'] = isset($data['note']) && !empty($data['note']) ? $data['note'] : "";
		return $returnRes;
	}
	/**
	 * 整理异常工单商品添加数据
	 * @param array $data
	 */
	public function getWorkOrderGoodsData($data){
		foreach($data['goods_id'] as $k => $v){
			$insertData['work_id'] = $data['work_id'];
			$insertData['goods_id'] = $v;
			$insertData['goods_sn'] = $data['goods_sn'][$k];
			$insertData['supplier_id'] = $data['supplier_id'][$k];
			$insertData['need_stock_type'] = $data['stock_type'][$k];
			$insertData['bom_id'] = $data['bom_id'][$k];
			$insertData['batch_no'] = $data['batch_no'][$k];
			$insertData['goods_number'] = $data['goods_number'][$k];
			$insertData['add_time'] = time();
			$allData[] = $insertData;
		}
		return $allData;
	}
	/**
	 * 保存指令工单操作信息
	 * @param array $param
	 */
	public function saveWorkOrderOperateData($param){
		$resData['work_id'] = $param['work_id'];
		$mainData['complete_time'] = $param['complete_time'];
		$mainData['note'] = $param['note'];
		$mainData['work_type'] = $param['work_type'];
		$resData['mainData'] = $mainData;
		$back_warehouse_id = $param['abnormalOpData']['back_warehouse_id'];
		$bearParty = $param['abnormalOpData']['bear_party'];
		$bearPrice = $param['abnormalOpData']['bear_price'];
		if($param['work_type'] < WORK_ORDER_SELECT_TYPE){
			if(!empty($bearParty) && !empty($bearPrice)){
				foreach($bearParty as $k => $v){
					if($bearParty[$k] == "暂无责任方"){
						continue;
					}
					$o['bear_party'] = $bearParty[$k];
					$o['bear_price'] = $bearPrice[$k];
					$o['back_warehouse_id'] = $back_warehouse_id;
					$o['work_id'] = $param['work_id'];
					$o['add_time'] = time();
					$needData[] = $o;
				}
			}else{
				$o['bear_party'] = "暂无责任方";
				$o['bear_price'] = 0;
				$o['back_warehouse_id'] = $param['abnormalOpData']['back_warehouse_id'];
				$o['work_id'] = $param['work_id'];
				$o['add_time'] = time();
				$needData[] = $o;
			}
			$insertData['abnormalOpData'] = $needData;
			$insertData['flag'] = WORK_ORDER_TYPE_FROM_ABNORMAL;
		}else{
			if(isset($param['workOpData']) && !empty($param['workOpData'])){
				$insertData['workOpData'] = $param['workOpData'];
				$insertData['workOpData']['work_id'] = $param['work_id'];
				$insertData['workOpData']['add_time'] = time();
				$insertData['flag'] = WORK_ORDER_TYPE_ADD;
			}else{
				$insertData = array();
			}
		}
		$resData['insertData'] = $insertData;
		return $resData;
	}
	
	/**
	 * 保存指令工单操作信息数据
	 * @param array $param
	 */
	public function saveWorkOrderOperateDo($data){
		$map = array(
				array('key' => 'id', 'op' => '=', 'value' => $data['work_id']),
		);
		$this->db->query('ecs_third_work_order', $data['mainData'], 'UPDATE', $map);
		if($data['insertData']['flag'] == WORK_ORDER_TYPE_FROM_ABNORMAL){
			//异常通知处理方案 存入表ecs_third_abnormal_order_item
			$isDelete['is_delete'] = 1;
			$map1 = array(
					array('key' => 'work_id', 'op' => '=', 'value' => $data['work_id']),
			);
			$this->db->query('ecs_third_abnormal_order_item', $isDelete, 'UPDATE', $map1);
			$otherRes = $this->db->query('ecs_third_abnormal_order_item', $data['insertData']['abnormalOpData'], 'BATCH_INSERT');
		}else{
			//新增指令工单处理方案 存入表ecs_third_work_order_item
			$isDelete['is_delete'] = 1;
			$map2 = array(
					array('key' => 'work_id', 'op' => '=', 'value' => $data['work_id']),
			);
			$this->db->query('ecs_third_work_order_item', $isDelete, 'UPDATE', $map2);
			if(!empty($data['insertData'])){
				$otherRes = $this->db->query('ecs_third_work_order_item', $data['insertData']['workOpData'], 'INSERT');
			}else{
				return $this->tools->j(SUCCESS, "保存方案成功");
			}
		}
		if($otherRes['error']){
			return $this->tools->j($otherRes['error'], $otherRes['msg']);
		}else{
			return $this->tools->j(SUCCESS, "保存方案成功");
		}
	}
	
	/**
	 * 更改指令工单状态
	 * @param int $workId
	 * @param int $status
	 */
	public function changeWorkOrderStatus($workId, $status){
		$data['status'] = $status;
		$map = array(
				array('key' => 'id', 'op' => '=', 'value' => $workId),
		);
		$res = $this->db->query('ecs_third_work_order', $data, 'UPDATE', $map);
		if(!$res['error']){
			return $this->tools->j(SUCCESS, "操作成功");
		}else{
			return $this->tools->j(ERROR, "操作失败");
		}
	}
	
	/**
	 * 删除指令工单商品
	 * @param int $workId
	 * @param int $status
	 */
	public function deleteWordOrderGoods($workId, $rec_id){
		$data['is_delete'] = 1;
		$map = array(
				array('key' => 'id', 'op' => '=', 'value' => $rec_id),
		);
		$res = $this->db->query('ecs_third_work_order_goods', $data, 'UPDATE', $map);
		if(!$res['error']){
			return $this->tools->j(SUCCESS, "操作成功");
		}else{
			return $this->tools->j(ERROR, "操作失败");
		}
	}
	
	/**
	 * 插入数据表ecs_work_order_goods
	 * @param array $data
	 */
	public function insertWorkOrderGoods($data){
		$res = $this->db->query('ecs_third_work_order_goods', $data, 'BATCH_INSERT');
		if($res['error']){
			return $this->tools->j($res['error'], $res['msg']);
		}else{
			return $this->tools->j(SUCCESS, "新增商品成功");
		}
	}
	
	/**
	 * 插入数据表ecs_work_order
	 * @param array $data
	 */
	public function insertWorkOrder($data){
		$res = $this->db->query('ecs_third_work_order', $data, 'INSERT');
		if($res['error']){
			return $this->tools->j($res['error'], $res['msg']);
		}else{
			if($data['abnormal_id'] > 0){
				//指令单上若异常单ID大于0  直接获取异常单上的商品信息存入指令工单商品表
				$goodsData['work_id'] = $res['msg'];
				$goodsData['goods_id'] = $data['goods_id'];
				$goodsData['goods_sn'] = $data['goods_sn'];
				$goodsData['supplier_id'] = $data['supplier_id'];
				$goodsData['need_stock_type'] = 0;
				$goodsData['bom_id'] = $data['bom_id'];
				$goodsData['batch_no'] = $data['batch_no'];
				$goodsData['goods_number'] = $data['goods_number'];
				$goodsData['add_time'] = time();
				$this->db->query('ecs_third_work_order_goods', $goodsData, 'INSERT');
			}
			return $this->tools->j(SUCCESS, "新增指令工单成功", $res['msg']);
		}
	}
	
	/**
	 * 根据配套池和路径获取批次
	 * @param int $orderId
	 * @param string $resStr
	 * @param array $getSortRegionWarehouse 预配货的路径信息 期望到货时间信息
	 */
	public function getPoolBatchNo($orderId, $resStr, $getSortRegionWarehouse){
		if(empty($orderId) || empty($resStr) || empty($getSortRegionWarehouse)){
			return array();
		}
		//首先验证该订单是否应该清除已经预配货的信息(订单商品信息被修改);
		$CanRemovePre['s_order_id'] = $orderId;
		$canRemovePreRes = $this->tools->callRule('Logistics_HUV1_PreDisDelete', $CanRemovePre);
		if($canRemovePreRes['error']) {
			$this->addRecordWithPreOrder($orderId, "判断是否清除预配货时，规则Logistics_HUV1_PreDisDelete返回信息错误，".$canRemovePreRes['msg'], "规则出错");
		}
		$h_is_delete = is_array($canRemovePreRes['msg']) ? $canRemovePreRes['msg'][0]['h_is_delete'] : 1;
		//如果需要对该订单已经生成的预配货记录进行清除
		if($h_is_delete){
			$noteInfoDelete .= "需要进行预配货清除操作, ";
			//如果为现货的话需要还原库存
			$returnRes = $this->returnStock($orderId);
			$this->addRecordWithPreOrder($orderId, $noteInfoDelete.$returnRes['msg']);
		}
		//--------------------清除工作完成后, 进行配货操作---------------------------
		//验证该订单是否支持部分发货
		$isPartialShipment = $this->checkOrderIsPartialShipment($getSortRegionWarehouse);
		//存放每一个分组获取的配货信息
		$preInfo = array();
		//分组后返回的预配货信息为空且不支持部分发货 的标记
		$orderFlag = 0;
		//处理客户期望到货时间
		$planSendTime = explode(",", $getSortRegionWarehouse['h_plan_send_time']);
		sort($planSendTime);
		$stockInfo = array();
		$stockFuturesInfo = array();
		$outOfStockInfo = array();
		//循环时间分组,对每一个分组的订单商品进行配货,并将配货信息叠加存放在一起
		foreach($planSendTime as $k => $v){
			//区域父级仓库, 多个用逗号隔开
			$param1['s_warehouse_id'] = $getSortRegionWarehouse['h_warehouse_id'];
			//每一个分组
			$param1['s_pre_ship_time'] = $v;
			//订单ID
			$param1['s_order_id'] = $orderId;
			//排序后的路径信息
			$param1['s_warehouse_unload'] = $resStr;
			//规则返回的路径信息
			$param1['s_region_id'] = $getSortRegionWarehouse['h_region_id'];
			//规则返回的库存ID及数量信息
			$param1['s_already_number'] = json_encode($stockInfo) == "[]" ? "" : json_encode($stockInfo);
			//日志存储规则调用的参数
			$this->addRecordWithPreOrder($orderId, "第".($k+1)."组预配货参数为:".implode("---", $param1));
			$resOne = $this->tools->callRule('Logistics_HUV1_PreDistribution', $param1);
			//日志存储规则返回的json结果,如果长度大于450,则分割 根据"},"分割存储
			if(strlen($resOne['msg'][0]['h_stock_info']) > PRE_DISTRIBUTION_LOG_LENGTH){
				$jsonExplode = explode("},", $resOne['msg'][0]['h_stock_info']);
				foreach($jsonExplode as $explode){
					$this->addRecordWithPreOrder($orderId, "第".($k+1)."组预配货现货结果为:".$explode, "预配货结果");
				}
			}else{
				if(!empty($resOne['msg'][0]['h_stock_info'])){
					$this->addRecordWithPreOrder($orderId, "第".($k+1)."组预配货现货结果为:".$resOne['msg'][0]['h_stock_info'], "预配货结果");
				}
			}
			if($resOne['error']) {
				$this->addRecordWithPreOrder($orderId, "预配货进行期间，规则Logistics_HUV1_PreDistribution返回信息错误，".implode("---", $resOne['msg']), "规则出错");
				continue;
			}
			$stockInfo = json_decode($resOne['msg'][0]['h_stock_info'], true);
			//如果分组返回信息为空且不支持部分发货，则该订单预配货失败
			if(empty($stockInfo) && sizeof($planSendTime) > 1 && !$isPartialShipment){
				$this->addRecordWithPreOrder($orderId, "不支持部分发货, 停止配货", "不支持部分发货");
				$orderFlag++;
				//退出循环, 直接返回空的预配货信息
				break;
			}
			//规则返回的期货信息和缺货信息
			$stockFuturesInfo = $resOne['msg'][0]['h_futures_info'] ? json_decode($resOne['msg'][0]['h_futures_info'], true) : array();
			$stockFuturesInfo = array_merge($stockFuturesInfo, $stockFuturesInfo);
			$outOfStockInfo = $resOne['msg'][0]['h_shortages_info'] ? json_decode($resOne['msg'][0]['h_shortages_info'], true) : array();
			//若存在重复的缺货记录 只记录相同客户期望到货时间的
			if($outOfStockInfo){
				$outOfStockInfo[$outOfStockInfo['plan_send_time']] = $outOfStockInfo;
			}
			//如果存在相同批次的记录,叠加扣减数量
			foreach($stockInfo as $value){
				//将每个分组返回的结果放入数组
				$preInfo[] = $value;
				//相同批次的叠加数量
				 $opStockInfo[$value['batch_id']] += $value['goods_number'];
			}
			//得到叠加后的新数组
			foreach($opStockInfo as $key1 => $value1){
				$stockInfo[$key1] = (string)$value1;
			}
		}
		//将每次返回的期货和缺货信息存入数组
		$all['preInfo'] = $preInfo;
		$all['stockFuturesInfo'] = $stockFuturesInfo ? $stockFuturesInfo : array();
		$all['outOfStockInfo'] = $outOfStockInfo ? $outOfStockInfo : array();
		$noteInfoResult .= "获得预配货现货信息:".count($preInfo)."条, ";
		$noteInfoResult .= "获得预配货期货信息:".count($stockFuturesInfo)."条, ";
		$noteInfoResult .= "获得预配货缺货信息:".count($outOfStockInfo)."条";
		if($preInfo || $stockFuturesInfo || $outOfStockInfo){
			$this->addRecordWithPreOrder($orderId, $noteInfoResult);
		}
		//返回所有预配货 现货 期货 缺货结果
		return $orderFlag > 0 ? array() : $all;
	}
	
	/**
	 * 添加预配货记录
	 * 添加预配货记录的同时,占用库存数量,若添加不成功,则取消占用
	 * @param int $orderId
	 * @param array $data
	 */
	public function addPreDistributionRecord($orderId, $data){
		if(empty($data['stockFuturesInfo']) && empty($data['outOfStockInfo']) && empty($data['preInfo'])){
			return $this->tools->j(SUCCESS, "处理成功");
		}
		//该订单预配货库存扣减是否全部成功
		$flag = 0;
		//扣减库存失败的记录的客户期望到货时间
		$failRecordPlanTime = 0;
		//将期货和缺货记录存入预配货信息
		$otherArr = array_merge($data['stockFuturesInfo'], $data['outOfStockInfo']);
		if(!empty($otherArr)){
			foreach($otherArr as $vv){
				$vv ['add_time'] = time ();
				$this->db->query('ecs_third_pre_distribution', $vv, 'INSERT');
			}
		}
		if(empty($data['preInfo'])){
			return $this->tools->j(SUCCESS, "处理成功");
		}
		//处理返回的预配货信息--保持和订单商品一致
		$preInfoData = $this->getNeedStyleData($data['preInfo']);
		//所有待添加的预配货记录
		foreach($preInfoData as $v){
			$v ['add_time'] = time ();
			$v ['goods_rec_id'] = $v ['rec_id'];
			//将预配货记录存入表
			$insertRes = $this->db->query('ecs_third_pre_distribution', $v, 'INSERT');
			if(!$insertRes['error']){
				//添加记录成功
				$note = "预配货记录：订单[ID:".$v['order_id']."]预配货记录生成成功";
				//成功之后进行库存扣减
				$changeRes = $this->changeStockGoodsNumber($v);
				if(!$changeRes['error']){
					//扣减库存成功
					$note .= ", 库存扣减成功！";
					//记录生成日志
					$this->addStockWarehouseAction($v['order_id'], $v['goods_id'], $v['plan_send_time'], $note);
				}else{
					$flag++;
					//获取失败的记录的客户期望到货时间
					$failRecordPlanTime = $v['plan_send_time'];
					$note = "扣减库存出错，".$note;
					//库存扣减失败
					$note .= ", 但是库存扣减失败, 原因为".$changeRes['msg'];
					$isDelete['is_delete'] = 1;
					$mapDelete = array(
							array('key' => 'id', 'op' => '=', 'value' => $insertRes['msg']),
							array('key' => 'is_delete', 'op' => '=', 'value' => 0),
							array('key' => 'send_status', 'op' => '=', 'value' => 0),
					);
					//清除刚刚插入的那条记录
					$this->db->query('ecs_third_pre_distribution', $isDelete, 'UPDATE', $mapDelete);
					//记录生成日志
					$this->addStockWarehouseAction($v['order_id'], $v['goods_id'], $v['plan_send_time'], $note);
					//扣减失败后, 不在进行该订单的预配货操作
					break;
				}
			}else{
				//添加记录失败
				$this->addStockWarehouseAction($v['order_id'], $v['goods_id'], $v['plan_send_time'],
						"订单[ID:".$v['order_id']."]预配货记录生成失败, 库存未扣减");
			}
		}
		//如果订单存在某一个商品库存未扣减成功, 则需将该组已经配货的记录清空
		if($flag > 0){
			//还原扣减失败之前该订单已经扣除的库存
			$returnRes = $this->returnStock($orderId, $failRecordPlanTime);
			//添加日志
			$this->addRecordWithPreOrder($orderId, $returnRes['msg'], "库存扣减失败后删除预配货记录并还原库存");
		}
		return $this->tools->j(SUCCESS, "处理成功");
	}
	/**
	 * 清除该订单在预配货的记录
	 * @param int $orderId
	 * @param int $preMainId 主键ID
	 */
	public function changePreDistributionStatus($orderId, $preMainId){
		$isDelete['is_delete'] = 1;
		//存在订单ID不存在客户期望到货时间则根据订单删除预配货记录
		if($orderId){
			$mapDelete = array(
					array('key' => 'order_id', 'op' => '=', 'value' => $orderId),
					array('key' => 'is_delete', 'op' => '=', 'value' => 0),
					array('key' => 'send_status', 'op' => '=', 'value' => 0),
			);          
		}else{
			//存在预配货主键ID则根据预配货主键删除预配货记录
			$mapDelete = array(
					array('key' => 'id', 'op' => '=', 'value' => $preMainId),
					array('key' => 'is_delete', 'op' => '=', 'value' => 0),
			);          
		}
		//清除该订单已经生成且未推送到物流系统的所有预配货记录
		$removeRes = $this->db->query('ecs_third_pre_distribution', $isDelete, 'UPDATE', $mapDelete);
		return $removeRes;
	}
	/**
	 * 验证订单是否支持部分发货(收货地址所在仓库为厦门和武汉(系统变量控制)的话不区分)
	 * @param array $getSortRegionWarehouse 订单信息
	 */
	public function checkOrderIsPartialShipment($getSortRegionWarehouse){
		//系统变量--特殊仓库--third_special_warehouse
		$thirdSpecialWarehouse = $this->tools->getDBVariableFromRule('third_special_warehouse');
		$thirdSpecialWarehouse = explode(",", $thirdSpecialWarehouse);
		//如果订单收货地址所属仓库在特殊仓库里
		$flag = 0;
		//多个覆盖仓库需要拆分
		foreach(explode(",", $getSortRegionWarehouse['h_warehouse_id']) as $v){
			if(in_array($v, $thirdSpecialWarehouse)){
				$flag++;
				break;
			}
		}
		if($flag > 0){
			return TRUE;
		}else{
			//订单整体发货则不支持
			return $getSortRegionWarehouse['ship_all'] == 1 ? FALSE : TRUE;
		}
	}
	
	/**
	 * 获取订单在预配货记录表的信息
	 * @param array $orderId
	 * @param int $planTime
	 */
	public function getOrderPreDistribution($orderId, $planTime=0){
		$param['n_order_id'] = $orderId;
		if(!empty($planTime)){
			$param['n_plan_send_time'] = $planTime;
		}
		//未删除
		$param['n_is_delete'] = 0;
		//未同步到物流系统
		$param['n_send_status'] = 0;
		$resOne = $this->tools->callRule('Logistics_BUV1_ThirdPreDistribution', $param);
		if($resOne['error']) {
			return array();
		}
		return $resOne['msg'];
	}
	/**
	 * 清除订单预配货信息的时候  库存还原
	 * @param int $orderId
	 */
	public function returnStock($orderId, $planTime=0){
		//获取该订单在预配货记录表里所有已经记录的信息
		$orderPreInfo = $this->getOrderPreDistribution($orderId, $planTime);
		if(empty($orderPreInfo)){
			return $this->tools->j(ERROR, "没有记录信息");
		}
		$success = array();
		$error = array();
		//对每一条记录的信息进行库存变更
		foreach($orderPreInfo as $k => $v){
			//除了现货  其他不用对库存进行还原操作
			if($v['pre_type'] != 1){
				continue;
			}
			//还原库存
			$changeRes = $this->changeStockGoodsNumber($v, 1);
			if(!$changeRes['error']){
				$success[] = "[订单ID：".$v['order_id']."][商品ID：".$v['goods_id']."][客户期望到货时间：".$v['plan_send_time']."]库存还原成功！";
			}else{
				//库存还原失败
				$error[] = "[订单ID：".$v['order_id']."][商品ID：".$v['goods_id']."][客户期望到货时间：".$v['plan_send_time']."]库存还原失败, 原因为".$changeRes['msg'];
			}
		}
		//如果全部还原库存成功, 将生成的预配货记录删除
		if(empty($error)){
			//如果是订单, 则对整个订单预配货记录进行删除
			$removeRes = $this->changePreDistributionStatus($orderId);
			if(!$removeRes['error']){
				$note .= "清除订单预配货记录成功";
			}else{
				$note .= "清除订单预配货记录失败".$removeRes['msg'];
			}
		}
		$res = array_merge($success, $error);
		$res = $note . ", ". implode(",", $res);
		return $this->tools->j(SUCCESS, $res);
	}
	
	/**
	 * 根据订单获取订单所在区域及仓库配送路径  然后排序
	 * @param unknown $orderId
	 */
	public function getSortRegionWarehouse($orderId){
		$param['order_id'] = $orderId;
		$res = $this->tools->callRule('Logistics_HUV1_OrderWarehouse', $param);
		if($res['error'] || !$res['msg']) {
			return array();
		}
		$res['msg'][0]['h_unload'] = json_decode($res['msg'][0]['h_unload'], true);
		foreach($res['msg'][0]['h_unload'] as $k => $v){
			$res['msg'][0]['h_unload'][$k]['district'] = $res['msg'][0]['district'];
		}
		return $res['msg'][0];
	}
	
	/**
	 * 直接根据获取的覆盖区域的物流周期从小到大排序
	 * @param array $opData
	 */
	public function otherOpreateSortOrder($opData){
		$all = array();
		$district = $opData[0]['district'];
		foreach($opData as $v){
			$arr = explode(",", $v['unload']);
			$all = array_merge($all, $arr);
		}
		$all = array_unique(array_filter($all));
		foreach($all as $value){
			$cycle = $this->getWarehouseRegionCycle($value, $district);
			$newArr[$value] = $cycle;
		}
		asort($newArr);
		foreach($newArr as $kk => $vv){
			$newResArr[] = $kk;
		}
		$returnData = implode(",", $newResArr);
		return $returnData;
	}
	
	/**
	 * 根据收货地址所在区域进行路径解析
	 * 按照物流周期由小到大
	 * 区域的每一个父级需查找完成后在从另外一个父级查找
	 * @param array $opData
	 */
	public function opreateSortOrder($opData){
		if(!class_exists('cls_cache_new')) {
			require ROOT_PATH . 'include/cls_cache.php';
		}
		//memcache操作类
		$memSetting = $this->tools->getDBVariableFromRule('admin_memcache_setting');
		$memSetting = explode(':', $memSetting);
		$this->cache = new cls_cache_new($memSetting[0], $memSetting[1]);
		//先从缓存中获取解析的路径, 存在则直接使用解析的路径
		$flag = $opData[0]['district'];
		if($memSetting){
			$memData = $this->cache->getMem('orderWarehouseRegion_'.$flag);
			$memData = json_decode($memData, true);
			if(!empty($memData)){
				return $memData;
			}
		}
		//不存在则解析
		$all = array();
		foreach($opData as $v){
			$arr = explode(",", $v['unload']);
			$all = array_merge($all, $arr);
		}
		$all = array_unique($all);
		$newData = array();
		//转换成 类似无限极分类 的格式并按照物流周期进行排序
		foreach($all as $es){
			$parent = $this->getParentWarehouse($es, $opData[0]['district'], 0);
			foreach($parent as $ggg){
				$data['id'] = $ggg;
				$data['pid'] = $es;
				$data['cycle'] = $this->getWarehouseRegionCycle($ggg, $opData[0]['district']);
				$newData[$data['id']] = $data;
			}
		}
		$data1['id'] = $all[0];
		$data1['pid'] = 0;
		$data1['cycle'] = $this->getWarehouseRegionCycle($all[0], $opData[0]['district']);
		$newData[$data1['id']] = $data1;
		$this->newArr = array();
		$res = $this->getChild($newData);
		//按照需求对每一个排序内在进行排序
		$res = $this->allSort($res);
		$res = array_filter($res);
		$resStr = implode(",", $res);
		//将解析的结果存入缓存
		if($memSetting) {
			$this->cache->setMem('orderWarehouseRegion_'.$flag, $resStr);
		}
		return $resStr;
	}
	
	/**
	 * 变更商品库存
	 * @param array $data
	 * @param int $type 0--减少库存 1-还原库存(增加)
	 */
	public function changeStockGoodsNumber($data, $type=0){
		$batch_id = $data['batch_id'];
		if(!isset($batch_id) || $batch_id <= 0){
			return $this->tools->j(ERROR, "库存信息有误。");
		}
		$param['n_batch_id'] = $batch_id;
		$param['n_is_delete'] = 0;
		$res = $this->tools->callRule('Logistics_BUV1_ThirdStockInfo', $param);
		if($res['error']) {
			return $this->tools->j(ERROR, "规则返回信息错误。错误信息为: ". $res['error']. "参数为：".implode(",", $param));
		}
		//如果还原库存时, 需要还原的预配货记录商品数量大于库存占用
		if($type && $data['goods_number'] > $res['msg'][0]['pre_use_number']){
			return $this->tools->j(SUCCESS, "库存未还原, 原因为预配货商品数量大于占用库存");
		}
		//如果占用库存时, 占用库存大于等于库存总数时, 不往下操作
		if(!$type && $res['msg'][0]['goods_number'] <= $res['msg'][0]['pre_use_number']){
			return $this->tools->j(ERROR, "占用库存失败，库存占用不足");
		}
		//变更数量
		if(!$type){
			//占用库存  就是把 pre_use_number增加
			$updateData = array('pre_use_number' => '$EP.pre_use_number+'.$data["goods_number"]);
		}else{
			//还原库存  就是把 pre_use_number减少
			$updateData = array('pre_use_number' => '$EP.pre_use_number-'.$data["goods_number"]);
		}
		$map = array(
				array('key' => 'batch_id', 'op' => '=', 'value' => $batch_id),
		);
		$res = $this->db->query('ecs_third_logistics_stock', $updateData, 'UPDATE', $map);
		if(!$res['error']){
			$param5['n_batch_id'] = $batch_id;
			$resBatch = $this->tools->callRule('Logistics_BUV1_ThirdStockInfo', $param5);
			if(!$resBatch['error'] && $type) {
				$this->addStockWarehouseAction(0, 0, 0, "扣减库存成功,订单[ID:".$data['order_id']."]商品[ID:]".$data['goods_id']."扣减批次[".$batch_id."][".$data['goods_number']."]个,剩余".($resBatch['msg'][0]['goods_number']-$resBatch['msg'][0]['pre_use_number'])."个");
			}
			return $this->tools->j(SUCCESS, "库存变更成功！");
		}else{
			return $this->tools->j(ERROR, "库存变更失败。");
		}
	}
	
	/**
	 * 保存预配货生成日志
	 * @param int $orderId  订单ID
	 * @param int $goodsId  商品ID
	 * @param int $planTime  客户期望到货时间
	 * @param string $note       日志内容
	 * @param string $type   操作类型
	 */
	public function addStockWarehouseAction($orderId, $goodsId, $planTime, $note, $type="thirdPreDistribution") {
		if(!$note) {
			return;
		}
		$data = array(
				'add_time' => time(),
				'user_id' => 'system',
				'rec_id' => $orderId,
				'note' => $note,
				'type' => $type,
		);
		if($orderId && $goodsId && $planTime){
			$data['other'] = "[".$orderId."][".$goodsId."][".$planTime."]";
		}else{
			$data['other'] = "[订单ID:".$orderId."]";
		}
		$this->db->query('ecs_stock_warehouse_action', $data, 'INSERT');
	}
	
	/**
	 * 获取所在区域到仓库的物流周期
	 * @param int $warehouseId
	 * @param int $regionId
	 */
	public function getWarehouseRegionCycle($warehouseId, $regionId){
		$param['n_start_region_id'] = $warehouseId;
		$param['n_end_region_id'] = $regionId;
		$res = $this->tools->callRule('Logistics_BUV1_ThirdWarehouseRegion', $param);
		if($res['error'] || empty($res['msg'])) {
			return '0';
		}
		return $res['msg'][0]['cycle'];
	}
	/**
	 * 获取该仓库的父级并排序
	 * @param int $warehouseId
	 */
	public function getParentWarehouse($warehouseId, $regionId, $needSort = false){
		$param['n_warehouse_id'] = $warehouseId;
		$res = $this->tools->callRule('Logistics_BUV1_ThirdCoverInfo', $param);
		if($res['error'] || !$res['msg']) {
			return array();
		}
		$parent = array();
		foreach($res['msg'] as $v){
			$parent[] = $v['parent_warehouse_id'];
		}
		return $parent;
	}
	
	/**
	 * 处理多级
	 * @param array $items
	 * @return multitype:
	 */
	public function getChild($items){
		foreach ($items as $item){
			$items[$item['pid']]['son'][$item['id']] = &$items[$item['id']];
		}
		return isset($items[0]['son']) ? $items[0]['son'] : array();
	}
	
	/**
	 * 多级排序 递归
	 * @param array $data
	 */
	public function allSort($data, $arr){
		$data = $this->diySort($data, 'cycle');
		foreach($data as &$v){
			$arr[] = $v['id'];
			if($v['son']){
				$v['son'] = $this->allSort($v['son'], $arr);
			}
		}
		$this->newArr = array_merge($this->newArr,$arr);
		return array_unique($this->newArr);
	}
	
	/**
	 * 多维排序
	 * @param array $data
	 * @param string $sort 按照什么排序
	 * @return array
	 */
	public function diySort($data, $sort){
		if(empty($data)){
			return array();
		}
		foreach($data as $v){
			$sorts[] = $v[$sort];
		}
		array_multisort($sorts, SORT_ASC, $data);
		return $data;
	}
	
	/**
	 * 订单排序 付款比例 订单生成时间 订单金额 尽快发货状态(尽快发货优先)
	 * @param array $data
	 * @return array
	 */
	public function orderSort($data){
		if(empty($data)){
			return array();
		}
		foreach($data as $v){
			$sortTime[] = $v['h_sort_time'];
			$addTime[] = $v['h_add_time'];
			$full[] = $v['h_pay_proportion'];
			$amount[] = $v['h_order_amount'];
			$hurry[] = $v['h_pre_ship_status'];
			$patchOrder[] = $v['h_order_type'];
		}
		array_multisort($patchOrder, SORT_ASC, $sortTime, SORT_ASC, $addTime, SORT_ASC, $full, SORT_DESC, $amount, SORT_DESC, $hurry, SORT_ASC,$data);
		return $data;
	}
	
	/**
	 * 获取所有符合要求的订单信息
	 * 已确认  有效(支付金额为订单金额的30%以上);
	 * 按照"尽快发货" 客户期望到货时间 下单时间 支付时间 订单金额排序
	 */
	public function getAllAllowOrderInfo(){
		$param = array(); 
		$res = $this->tools->callRule('Logistics_HUV1_PreDisOrder', $param);
		if($res['error'] || !$res['msg']) {
			return array();
		}
		$orders = $res['msg'];
		//客户期望到货时间 订单生成时间 按照付款比例  订单金额 尽快发货状态进行订单的排序
		$orders = $this->orderSort($orders);
		return $orders;
	}
	
	/**
	 * 获取所有符合要求的发货单信息
	 */
	public function getAllAllowInvoiceInfo(){
		$param = array();
		$res = $this->tools->callRule('Logistics_BUV1_InvoiceOrder', $param);
		return $res['error'] || !$res['msg'] ? array() : $res['msg'];
	}
	
	/**
	 * 根据订单获取该订单预配货的结果
	 * 成功返回预配货记录,失败返回缺货记录
	 * @param int $orderId
	 */
	public function getOrderGoodsStockFromPool($orderId){
		$param['n_order_id'] = $orderId;
		$res = $this->tools->callRule('Logistics_HUV1_TotalInfo', $param);
		if($res['error'] || !$res['msg']) {
			return array();
		}
		return $res['msg'];
	}
	/**
	 * 记录订单预配货时每一步产生的结果
	 */
	public function addRecordWithPreOrder($orderId, $note, $prefixNote = ""){
		$i = $prefixNote ? "，" : "";
		$this->addStockWarehouseAction($orderId, 0, 0, $prefixNote.$i."订单[ID:".$orderId."]".$note);
	}
	
	/**
	 * 验证是否可以操作
	 * @param string $admin_id    验证是否为后台用户
	 * @return array
	 */
	public function checkIsAdmin($admin_id) {
		$param['user_id'] = $admin_id;
		$res = $this->tools->callRule('Mll_Bsv1_AdminUser', $param);
		return $res['error'] || empty($res['msg']) ? $this->tools->j($res['error'], "获取用户信息失败") : $this->tools->j(SUCCESS, $res['msg']);
	}
	/**
	 * 将规则返回的预配货 现货信息转换为和订单商品行保持一致的格式
	 * 由于预配货的时候是将相同的商品合并在一起,然后去库存里面取数
	 * 所以配货后的数据是根据库存数据的数量合并的,
	 * 比如 该商品总数需要10个.可能是从1-10个不相同的批次里取出来的
	 * 现在需要将订单商品项和批次对应起来
	 * 
	 * 将数据全部换为数量为1的格式 然后根据rec_id和对应的数量  依次取够 并按照相同批次数量叠加组成最后的数组
	 * rec_id => array(batch_id, number)
	 * @param array $data
	 */
	public function getNeedStyleData($data){
		$returnData = array();
		$otherData = array();
		$arr2 = array();
		$numbers = array();
		$need_numbers = array();
		$need_rec_id = array();
		foreach($data as $v){
			$rec_ids = explode(",", $v['rec_id']);
			if(sizeof($rec_ids) <= 1){
				//如果返回的rec_id为单个  不处理
				$otherData[] = $v;
				continue;
			}else{
				unset($v['rec_id']);
				$num = $v['numbers'];
				$numbers = explode(",", $num);
				foreach($rec_ids as $rec => $id){
					//如果存在多个不同的rec_id数据 依次放入
					if(!in_array($id, $need_rec_id)){
						$need_rec_id[] = $id;
						$need_numbers[] = $numbers[$rec];
					}
				}
				$goods_number = $v['goods_number'];
				//所有需要拆分的批次
				//去掉商品数量
				unset($v['goods_number']);
				//numbers主要用与查询商品对应的数量 用来做拆分使用 用后则去掉
				unset($v['numbers']);
				for($a = 0; $a < $goods_number; $a++){
					$allData[] = $v;
				}
			}
		}
		//$allData 为所有带拆分的记录
		$c = 0;
		//根据订单商品的数量拆分 需要拆分的数组
		foreach($need_numbers as $aa){
			$arr1[] = array_slice($allData, $c, $aa);
			$c += $aa;
		}
		//拆分后按照订单商品行和数量  找出每个批次对应的商品行和数量
		foreach($arr1 as $key => $val){
			$i = 0;
			$useArr = array();
			foreach($val as $vv){
				//数量叠加
				$i++;
				$arr2[$key][$vv['batch_id']] = $vv;
				$arr2[$key][$vv['batch_id']]['rec_id'] = $need_rec_id[$key];
				//不同批次数量重置
				if(!in_array($vv['batch_id'], $useArr)){
					$useArr[] = $vv['batch_id'];
					$i = 1;
				}
				$arr2[$key][$vv['batch_id']]['goods_number'] = $i;
			}
		}
		$needData = array();
		//组装数组
		foreach($arr2 as $vaa){
			foreach($vaa as $vaaa){
				$needData[] = $vaaa;
			}
		}
		//合并需要拆分的和不需要拆分的为最后预配货的数据
		$returnData = array_merge($otherData, $needData);
		return $returnData;
	}
	
	/**
	 * 获取预配货记录详细信息
	 * @param int $id 预配货主键
	 */
	public function getPreInfoFromId($id){
		$param['n_id'] = $id;
		$param['n_is_delete'] = 0;
		$res = $this->tools->callRule('Logistics_BUV1_ThirdPreDistribution', $param);
		if($res['error']) {
			return $this->tools->j(ERROR, "规则返回信息错误。错误信息为: ". $res['msg']);
		}
		return $this->tools->j(SUCCESS, $res['msg'][0]);
	}
	
	//预配货脚本添加锁
	public function getPreDistributionOrderLock($lockname){
		$lock_tools = new Tools();
		$expireTime = $this->tools->getDBVariableFromRule('PreDistributionExpireTime');
		$this->addStockWarehouseAction(0, 0, 0, date('Y-m-d H:i:s', time())."预配货加锁, 锁名为：".$lockname);
		$locked = $lock_tools->getLock($lockname, $expireTime);
		return $locked;
	}
	/*
	 * 获取memcache缓存内容
	 */
	public function getMemcacheData($flag){
		if(!class_exists('cls_cache_new')) {
			require ROOT_PATH . 'include/cls_cache.php';
		}
		//memcache操作类
		$memSetting = $this->tools->getDBVariableFromRule('admin_memcache_setting');
		$memSetting = explode(':', $memSetting);
		$this->cache = new cls_cache_new($memSetting[0], $memSetting[1]);
		$memData = $this->cache->getMem($flag);
		$memData = json_decode($memData, true);
		return $memData;
	}
	/**
	 * 添加预配货方法执行限制,每次只能一个进程运行该脚本
	 * 
	 */
	public function addSentryAction($invoice_id, $type = "PreDistributionOrder"){
		if(empty($invoice_id)){
			return 0;
		}
		$data['invoice_id'] = $invoice_id;
		$data['parent_id'] = 0;
		$data['add_time'] = time();
		$data['end_time'] = 0;
		$data['type'] = $type;
		$data['is_end'] = 0;
		$key = $this->db->query('ecs_invoice_sentry_action', $data, 'INSERT');
		return $key['msg'];
	}
	/**
	 * 当任务执行结束后,重置为初始状态
	 * 以便进行下一次脚本的运行
	 * 如果异常中断,半小时释放一次
	 */
	public function endSentryAction($sentryId=0, $change=false){
		if (!$sentryId && !$change) {
			return false;
		}
		//如果需要自动重置
		if($change){
			//获取预配货监控异常未重置的记录
			$getSentryInfo = $this->getSentryInfo(0,"PreDistributionOrder");
			$sentryId = $getSentryInfo['id'];
		}
		$map = array(
				array('key' => 'id', 'op' => '=', 'value' => $sentryId),
		);
		$update['end_time'] = time();
		$update['is_end']   = 1;
		//完成监控
		$mainRes = $this->db->query('ecs_invoice_sentry_action', $update, 'UPDATE', $map);
		return $mainRes['msg'];
	}
	/**
	 * 获取该监控信息
	 */
	public function getSentryInfo($sentryId=0, $type="", $isEnd=0){
		if($sentryId){
			$param['id'] = $sentryId;
		}
		if(!empty($type)){
			$param['type'] = $type;
		}
		$param['is_end'] = $isEnd;
		$res = $this->tools->callRule('M_BUV1_InvoiceSentryAction', $param);
		return $res['error'] || !$res['msg'] ? array() : $res['msg'][0];
	}
}